/**
 * Instances of this class have their counterparts in the players on board. These objects have information about
 * statistics (Stats stats)and has reference to current board (Board board). 
 * Class Player implements interface MoveableObject.
 * @author mx109
 *
 */
public class Player implements  MoveableObject {

	private int idp=0;
	private static int p=0;
	private Position pos;
	public Board board;
	public Stats stats = new Stats();
	private Direction direction=Direction.DOWN;
	private boolean addHistory=true;
	
	/**
	 * Create new player on chosen position in given board. 
	 * @param pos  position for new player
	 * @param b  board on which the player will be placed
	 */
	public Player(Position pos, Board b) {
		board=b;
		setPosition(pos);
		idp=p++;
	}
	/** Constructor used by Checker class
	 * 
	 * @author crezax
	 */
	protected Player(Position pos, Board b, boolean addHistory){
		setPosition(pos);
		board=b;
		this.addHistory = addHistory;
	}
	
	/** Constructor used by Checker class
	 * 
	 * @author crezax
	 */
	protected Player(Player p, Board b){
		idp = p.idp;
		pos = new Position(p.pos);
		board = b;
		direction = p.direction;
	}
	
	/** Equals method
	 * 
	 * @author crezax
	 */
	@Override
	public boolean equals(Object o){
		if (!(o instanceof Player)) return false;
		return idp == ((Player) o).idp;
	}
	
	
	@Override
	public boolean move(Direction dir) {
		Position p=new Position(this.pos.x, this.pos.y);
		if (dir==Direction.DOWN) p.y++;
		if (dir==Direction.UP) p.y--;
		if (dir==Direction.LEFT) p.x--;
		if (dir==Direction.RIGHT) p.x++;
		direction=dir;
		if (p.x>=board.rozx || p.x<0 || p.y>=board.rozy || p.y<0)return false;
		Field next=board.check(p);
		if (next.movObj instanceof Player || next.type==Type.WALL) return false;
		boolean boxMove=false;
		if (next.movObj!=null && next.movObj instanceof Box){
			boxMove=true;
		if (next.movObj.move(dir)==false) return false;}
		stats.addMove();
		if (boxMove) stats.addPush();
		board.check(pos).movObj=null;
		board.check(p).movObj=this;
		pos=p;
		if (addHistory)board.history.add(this.idp, dir, boxMove);
		addHistory=true;
		return true;
	}
	
	/**
	 * Revoke the player's move
	 * @param dir direction of revocation
	 * @param moveBox if true then this method try revoke move of pushed box
	 */
	public void moveBack(Direction dir, boolean moveBox){
		
		stats.backMove();
		stats.backMove();
		if (moveBox) {
			stats.backPush();
		}
		addHistory=false;
		move(dir);
	}
	
	@Override
	public Position getPosition() {return pos;}

	@Override
	public void setPosition(Position a) {this.pos=new Position(a.x,a.y);}
   
	protected static void setZero(){p=0;}
	
	@Override
	public int getId() {return idp;}

	/** A method, that tries to move player in given direction. If it is possible, player will pull a box after him.
	 * 
	 * @author crezax
	 * @param dir Direction player should move in.
	 * @return true if player pulled box after him, false otherwise.
	 */
	public boolean pullBack(Direction dir) {
		Position p = new Position(pos), q = new Position(pos);
		switch (dir){
		case UP:
			p.y--;
			q.y++;
			break;
		case DOWN:
			p.y++;
			q.y--;
			break;
		case LEFT:
			p.x--;
			q.x++;
			break;
		case RIGHT:
			p.x++;
			q.x--;
		}
		if(board.check(p).movObj != null) return false;
		if(board.check(p).type == Type.WALL) return false;
		move(dir);
		if(board.check(q).movObj instanceof Box)
			board.check(q).movObj.move(dir);
		return true;
	}

	/** A method, that moves player in given direction, but he will not move, if it would require pushing a box.
	 * 
	 * @param dir Direction player should move in.
	 */
	public void moveWithoutPushing(Direction dir) {
		Position p = new Position(pos), q = new Position(pos);
		switch (dir){
		case UP:
			p.y--;
			q.y++;
			break;
		case DOWN:
			p.y++;
			q.y--;
			break;
		case LEFT:
			p.x--;
			q.x++;
			break;
		case RIGHT:
			p.x++;
			q.x--;
		}
		if(board.check(p).movObj != null) return;
		if(board.check(p).type == Type.WALL) return;
		move(dir);
	}
}
